#include <memory.h>

#define LG_CORE

#include "lg.h"

typedef struct ListNode ListNode;
struct ListNode {
	unsigned count;
	unsigned cap;
	char* data;
	ListNode* next;
};

struct LGList {
	unsigned total;
	size_t size;
	ListNode node;
	ListNode* last;
	ListNode* iter;
};

static ListNode*
expandList(LGList* list) {
	ListNode* next = &list->node;
	while (next->next) {
		next = next->next;
	}
	unsigned cap = next->cap * 2;
	ListNode* n = malloc(sizeof(ListNode));
	n->cap = cap;
	n->count = 0;
	n->next = NULL;
	n->data = malloc(list->size * cap);
	next->next = n;
	list->last = n;
	return n;
}

LG_API LGList*
lgNewList(size_t size, unsigned cap) {
	LGList* list = malloc(sizeof(LGList));
	list->size = size;
	list->total = 0;
	ListNode* node = &list->node;
	list->iter = node;
	list->last = node;
	node->cap = cap;
	node->count = 0;
	node->next = NULL;
	node->data = malloc(size * cap);
	return list;
}

LG_API void
lgFreeList(LGList* list) {
	ListNode* next = &list->node;
	free(next->data);
	next = next->next;
	ListNode* temp;
	while (next) {
		temp = next->next;
		free(next->data);
		free(next);
		next = temp;
	}
	free(list);
}

LG_API void
lgPushList(LGList* list, void* p, unsigned count) {
	list->total += count;
	ListNode* node = list->last;
	unsigned copy = 0;
	unsigned offset = 0;
	size_t size = list->size;
	while (count > 0) {
		copy = node->cap - node->count;
		if (copy <= 0) {
			node = expandList(list);
			copy = node->cap;
		}
		if (copy > count) {
			copy = count;
		}
		memcpy(node->data + (size * node->count),
			(char*)p + (size * offset), size * copy);
		count -= copy;
		node->count += copy;
		offset += copy;
	}
}

LG_API void
lgClearList(LGList* list) {
	ListNode* next = &list->node;
	while (next) {
		next->count = 0;
		next = next->next;
	}
}

LG_API void
lgClearListCap(LGList* list) {
	ListNode* next = &list->node;
	next->count = 0;
	next = next->next;
	ListNode* temp;
	while (next) {
		temp = next->next;
		free(next->data);
		free(next);
		next = temp;
	}
}

LG_API void
lgBeginList(LGList* list) {
	list->iter = &list->node;
}

LG_API int
lgNextList(LGList* list, void** p, unsigned* count) {
	ListNode* iter = list->iter;
	if (!iter || !iter->count) {
		return 0;
	}
	*p = iter->data;
	*count = iter->count;
	list->iter = iter->next;
	return 1;
}

LG_API unsigned
lgGetListTotal(LGList* list) {
	return list->total;
}